#ifndef EXCEPTION_HPP
#define EXCEPTION_HPP 1

#include <cstdint>
#include <iostream>

namespace fur
{
   enum class ExceptionType
   {
      UNKNOWN,
      ALLOCATION,
      FILEREAD,
      RARHEAD,
      HEADCRC,
      FILECRC,
      UNPACK,
      UNSUPPORT
   };

   enum class ExceptionFlag
   {
      IGNORE,
      WARNING,
      EXCEPTION
   };

   ExceptionFlag &ef_rarhead() throw();
   ExceptionFlag &ef_headcrc() throw();
   ExceptionFlag &ef_filecrc() throw();

   template<class T> void exception_throw(ExceptionFlag flag, const T &exception) throw(T)
   // T should be a inherited class of Exception
   {
      switch (flag)
      {
	 case ExceptionFlag::IGNORE:
	    break;
	 case ExceptionFlag::WARNING:
	    std::cerr << "WARNING:"<< exception.message() << " in Function: " << exception.function() << std::endl;
	    break;
	 case ExceptionFlag::EXCEPTION:
	 throw exception;
      }
   }
   
   class Exception
   {
   public:

      //:functionname(funcname), exceptiontype(etype){};
      //~Exception() throw() {};
      const char *function() const throw();
      //{ return functionname;};
      ExceptionType type() const throw();
      //{ return exceptiontype;};
      virtual const char *what() const throw() = 0;
      const char *message() const throw();
      std::ostream &output(std::ostream &os = std::cerr) const throw();
   protected:
      Exception(ExceptionType exceptiontype, const char *functionname, const char *amessage) throw();
      ExceptionType etype;
      const char *fname;
      const char *mes;
   };

   class EUnknown:public Exception
   {
   public:
      EUnknown(const char *funcname) throw();
      const char *what() const throw();
   };
   
   class EAllocation:public Exception
   {
   public:
      EAllocation(const char *funcname) throw();
      const char *what() const throw();
   };
   
   class EFileread:public Exception
   {
   public:
      EFileread(const char *funcname) throw();
      const char *what() const throw();
   };
   
   class ERarhead:public Exception
   {
   public:
      ERarhead(const char *funcname) throw();
      const char *what() const throw();
   };
   
   class EHeadcrc:public Exception
   {
   public:
      EHeadcrc(const char *funcname) throw();
      const char *what() const throw();
   };
   
   class EFilecrc:public Exception
   {
   public:
      EFilecrc(const char *funcname) throw();
      const char *what() const throw();
   };
   
   class EUnpack:public Exception
   {
   public:
      EUnpack(const char *funcname) throw();
      const char *what() const throw();
   };
   
   class EUnsupport:public Exception
   {
   public:
      EUnsupport(const char *funcname) throw();
      const char *what() const throw();
   }; 
   
} // end of namespace fur

#endif /* exception.hpp */
